use reqwest::Body;
use serde::Deserialize;
use serde::Serialize;
use serde_json::{Value, json};
use std::collections::HashMap;
use tokio::fs::File;
use tokio_util::bytes::Bytes;
use tokio_util::codec::{BytesCodec, FramedRead};

use super::my_cache;
use super::my_err::MyErr;
use super::util_string;

#[derive(Deserialize)]
pub struct Ttx {
    pub domain: String,
    pub account: String,
    pub password: String,
}

// 车辆行驶里程接口返回的数据
#[allow(dead_code)]
pub struct RunMileageBean {
    pub mile: i64,          // 里程,单位: 米。
    pub veh_idno: String,   // 车牌号
    pub vehi_id: i64,       // 车辆ID
    pub s_time_str: String, // 上线时间
    pub e_time_str: String, // 下线时间
    pub ttime: i64,         // 在线时间(秒)
}

// 车辆所属机构数据
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
pub struct CompanyBean {
    pub id: Option<i64>,    // 公司ID
    pub nm: Option<String>, // 公司名称
    #[serde(rename = "pId")]
    pub pid: Option<i64>, // 上级机构ID
}

// 车辆数据
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
pub struct CarBean {
    pub id: Option<i64>,     // 车辆ID
    pub nm: Option<String>,  // 车牌号
    pub pid: Option<i64>,    // 车辆所属公司或机构ID
    pub pnm: Option<String>, // 车辆所属公司或机构名称
    pub dl: Vec<DevBean>,
}

// 设备信息
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
pub struct DevBean {
    pub id: Option<String>, // 设备号
    pub did: Option<i64>,   // 设备ID
}

// 车辆定位信息
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
pub struct GpsBean {
    pub id: Option<String>,   // 设备号
    pub vid: Option<String>,  // 车牌号
    pub gt: Option<String>,   // 定位上传时间
    pub hx: Option<i32>,      // 方向;正北方向为0度，顺时针方向增大，最大值360度。
    pub lng: Option<i64>,     // 经度;如果设备定位无效，值为0。 例如：113231258，真实值为113.231258
    pub lat: Option<i64>,     // 纬度;如果设备定位无效，值为0。 例如：113231258，真实值为113.231258
    pub mlng: Option<String>, // 地图经度;经过转换后的经度
    pub mlat: Option<String>, // 地图纬度;经过转换后的纬度
    pub sp: Option<i32>,      // 速度;单位: km/h，使用中需先除以10。
    pub tsp: Option<i32>,     // 行驶记录仪速度单位: km/h，使用中需先除以10 
    pub drid: Option<i64>,    // 司机ID
    pub jn: Option<String>,   // 司机资格证编码
}

// 车辆报警信息
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
pub struct AlarmBean {
    #[serde(rename = "DevIDNO")]
    pub dev_id: Option<String>, //  设备号
    pub desc: Option<String>,                //  报警信息
    pub guid: Option<String>,                //  报警唯一编号
    pub img: Option<String>,                 //  图片信息 用;分隔，为http地址路径，可以为多个图片信息。
    pub info: Option<rust_decimal::Decimal>, //  报警信息
    pub p1: Option<rust_decimal::Decimal>,   //  报警参数 1
    pub p2: Option<rust_decimal::Decimal>,   //  报警参数 2
    pub p3: Option<rust_decimal::Decimal>,   //  报警参数 3
    pub p4: Option<rust_decimal::Decimal>,   //  报警参数 4
    pub time: Option<String>,                //  报警时间
    #[serde(rename = "srcTm")]
    pub src_tm: Option<String>, //  源报警的时间
    #[serde(rename = "type")]
    pub alarm_type: Option<i32>, //  报警类型
    #[serde(rename = "Gps")]
    pub gps: Option<GpsBean>, // 定位信息
}

// 车辆报警附件信息
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
pub struct AlarmAttrInfo {
    pub vn: Option<String>,                //  	string	车牌号
    pub pl: Option<i32>,                   //  	number	车牌类型
    pub cn: Option<String>,                //  	string	公司名称
    pub sp: Option<i32>,                   //  	number	速度 单位: km/h，使用中需先除以10。
    pub dt: Option<i64>,                   //  	number	报警开始时间(UTC) 秒数
    pub lc: Option<String>,                //  	string	地理位置
    pub tp: Option<String>,                //  	string	报警类型
    pub jd: Option<rust_decimal::Decimal>, //  	number	经度 如果设备定位无效，值为0。 例如：113231258，真实值为113.231258
    pub wd: Option<rust_decimal::Decimal>, //  	number	纬度 如果设备定位无效，值为0。例如：39231258，真实值为39.231258
    pub dn: Option<String>,                //  	string	司机名称
    pub dc: Option<String>,                //  	string	司机驾驶证
    pub dp: Option<String>,                //  	string	司机电话
    pub ph: Option<String>,                //  	string	司机证件照
    pub phmd5: Option<String>,             //  	string	证件照md5
    pub dm: Option<String>,                //  	string	司机资格证编码
}

// 车辆报警附件文件
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
pub struct AlarmAttrFile {
    pub did: Option<String>,   //	string	设备号    当查询服务器上的录像时，则表示车牌号。
    pub fl: Option<String>,    //	string	文件路径
    pub flmd5: Option<String>, //	string	文件md5
    pub fsl: Option<String>,   //	string	获取文件流路径
    pub dsl: Option<String>,   //	string	下载路径,需要用户填写的参数：jsession(必须)SAVENAME(必须)
    pub st: Option<i32>,       //	number	服务器编号
    pub fo: Option<i64>,       //	number	文件偏移位置
    pub fs: Option<i64>,       //	number	文件大小
    pub ft: Option<i32>,       //	number	文件类型    0-图片 1 音视频- 2-音频 3-视频 4-文本 5-其他
    pub fb: Option<i64>,       //   number	文件开始时间
    pub fe: Option<i64>,       //	number	文件结束时间
    pub chn: Option<i32>,      //	number	设备通道    0表示通道1，1表示通道2。
}

// 车辆报警附件文件
#[derive(Deserialize, Debug, serde::Serialize)]
#[allow(dead_code)]
pub struct CarStatus {
    pub id: Option<String>,  // 设备号
    pub vid: Option<String>, // 车牌号,如果是用设备号查询，则为空。
    pub sp: Option<i32>,     // 速度 单位: km/h，使用中需先除以10。
    pub ol: Option<i32>,     //	在线状态 1表示在线，否则不在线。
    pub tsp: Option<i32>,    //	行驶记录仪速度单位: km/h，使用中需先除以10。执法仪版本表示电量
    pub dn: Option<String>,  //	司机名称
    pub jn: Option<String>,  //	司机资格证编码
    pub drid: Option<i64>,   //	司机id
    #[serde(rename = "driSw")]
    pub dri_sw: Option<i64>, //司机刷卡时间戳
    #[serde(rename = "driJn")]
    pub dri_jn: Option<String>, // 司机从业资格证编号
    #[serde(rename = "driSwStr")]
    pub dri_sw_str: Option<String>, //	司机刷卡时间
    pub dinfo: Option<String>, //	司机信息
}

// 用户登录URL
const LOGIN_URL: &str = "/StandardApiAction_login.action";
// 获取车辆行驶里程URL,只能查询隔天里程
const RUN_MILEAGE_URL: &str = "/StandardApiAction_runMileage.action";
// 获取用户车辆信息
const CAR_SEARCH_URL: &str = "/StandardApiAction_queryUserVehicle.action";
// 报警附件查询
const ALARM_EVIDENCE_URL: &str = "/StandardApiAction_alarmEvidence.action";
// 上传司机相片
const UPLOAD_DRIVER_IMG_URL: &str = "/WebuploaderApiAction_ajaxAttachUploadDriver.action";
// 新增、修改司机信息
const EDIT_DRIVER_URL: &str = "/DriverAction_mergeDriver.action";
// 获取设备状态（定位状态）
const DEVICE_STATUS_URL: &str = "/StandardApiAction_getDeviceStatus.action";
// TTS 发送TTS文字语音
const SEND_TTS_URL: &str = "/StandardApiAction_vehicleTTS.action";
// 新建联动报警
//const ADD_ALARM_URL: &str = "/StandardApiAction_mergeRule.action";

impl Ttx {
    /// TTS 发送TTS文字语音 (车辆必须在线才能操作)
    /// dev_id: 设备号
    /// tts_text： 语音文本内容
    /// flag: 标志如果参数为空，则默认值为4。紧急：1，终端显示器显示：4，终端TTS播读：8，广告屏显示：16。案例1：使用“紧急”、“终端显示器显示”，则值为：1+4=5。案例2：使用“终端TTS播读”、“广告屏显示”，则值为：4+8=12。
    #[allow(dead_code)]
    pub async fn add_alarm(&self, dev_id: &str, tts_text: &str, flag: usize) -> Result<bool, MyErr> {
        let jsession = self.get_jsession().await?;

        let form_data = json!({
            "jsession" : jsession,
            "DevIDNO":dev_id,
            "Text" : tts_text,
            "Flag" : flag,
        });

        #[derive(Deserialize, Debug)]
        pub struct RsBean {
            pub result: i32,
            pub status: Option<Vec<CarStatus>>,
        }

        let url = format!("{}{}", self.domain, SEND_TTS_URL);
        let rs = post_from_data_to_url(&url, &form_data).await;
        match rs {
            Ok(res) => {
                let result = res.get("result").and_then(Value::as_i64).unwrap_or(-100);
                if result == 0 {
                    Ok(true)
                } else {
                    Err(MyErr::Msg(format!("获取车辆状态失败！错误码：{} url:{} ", result, url)))
                }
            }
            Err(e) => Err(MyErr::Msg(format!("连接url地址失败！错误：{:?} url:{} ", e, url))),
        }
    }

    /// TTS 发送TTS文字语音 (车辆必须在线才能操作)
    /// dev_id: 设备号
    /// tts_text： 语音文本内容
    /// flag: 标志如果参数为空，则默认值为4。紧急：1，终端显示器显示：4，终端TTS播读：8，广告屏显示：16。案例1：使用“紧急”、“终端显示器显示”，则值为：1+4=5。案例2：使用“终端TTS播读”、“广告屏显示”，则值为：4+8=12。
    #[allow(dead_code)]
    pub async fn send_tts(&self, dev_id: &str, tts_text: &str, flag: usize) -> Result<i64, MyErr> {
        let jsession = self.get_jsession().await?;

        let form_data = json!({
            "jsession" : jsession,
            "DevIDNO":dev_id,
            "Text" : tts_text,
            "Flag" : flag,
        });

        #[derive(Deserialize, Debug)]
        pub struct RsBean {
            pub result: i32,
            pub status: Option<Vec<CarStatus>>,
        }

        let url = format!("{}{}", self.domain, SEND_TTS_URL);
        let rs = post_from_data_to_url(&url, &form_data).await;
        match rs {
            Ok(res) => {
                let result = res.get("result").and_then(Value::as_i64).unwrap_or(-100);
                if result == 0 {
                    let cmsserver = res.get("cmsserver").and_then(Value::as_i64).unwrap_or_default();
                    Ok(cmsserver)
                } else {
                    Err(MyErr::Msg(format!("获取车辆状态失败！错误码：{} url:{} ", result, url)))
                }
            }
            Err(e) => Err(MyErr::Msg(format!("连接url地址失败！错误：{:?} url:{} ", e, url))),
        }
    }

    /// 获取设备状态（定位状态）
    /// vehiIdno：车牌号 ， 可以是多个，以','分割。中文需要做url编码
    #[allow(dead_code)]
    pub async fn get_car_status(&self, vehi_idno: &str) -> Result<Vec<CarStatus>, MyErr> {
        let jsession = self.get_jsession().await?;

        let form_data = json!({
            "jsession" : jsession,
            "vehiIdno":vehi_idno,
            "driver" : 1, // 是否查询司机信息(姓名+工号)1: 查询; 其他或者不传: 不查询
        });

        #[derive(Deserialize, Debug)]
        pub struct RsBean {
            pub result: i32,
            pub status: Option<Vec<CarStatus>>,
        }

        let url = format!("{}{}", self.domain, DEVICE_STATUS_URL);
        let rs = post_from_data_to_url(&url, &form_data).await;
        match rs {
            Ok(res) => {
                if let Ok(rs_bean) = serde_json::from_value::<RsBean>(res) {
                    if rs_bean.result == 0 {
                        if let Some(vs) = rs_bean.status {
                            Ok(vs)
                        } else {
                            Err(MyErr::Msg(format!("获取车辆状态失败！缺少车辆状态！ url:{} ", url)))
                        }
                    } else {
                        Err(MyErr::Msg(format!("获取车辆状态失败！错误码：{} url:{} ", rs_bean.result, url)))
                    }
                } else {
                    Err(MyErr::Msg(format!("获取车辆状态失败！解析数据错误! url:{} ", url)))
                }
            }
            Err(e) => Err(MyErr::Msg(format!("连接url地址失败！错误：{:?} url:{} ", e, url))),
        }
    }

    /// 上传司机图片
    #[allow(dead_code)]
    async fn upload_driver_img_by_url(&self, driver_img_url: &str) -> Result<String, MyErr> {
        let response = reqwest::get(driver_img_url).await.map_err(|_| MyErr::Msg("获取网络文件失败！".to_string()))?;
        let bytes = response.bytes().await.map_err(|_| MyErr::Msg("获取网络文件内容失败！".to_string()))?;

        let file_body = bytes_to_body(bytes);

        let bio = reqwest::multipart::Part::stream(file_body)
            .file_name("driver.jpg")
            .mime_str("image/jpeg")
            .map_err(|e| MyErr::Msg(format!("读取上传文件失败：错误：{:?}！", e)))?;

        let form = reqwest::multipart::Form::new().part("upload", bio);

        let jsession = self.get_jsession().await?;

        let url = format!("{}{}?jsession={}", self.domain, UPLOAD_DRIVER_IMG_URL, jsession);

        let client = reqwest::Client::new();
        let rs = client.post(&url).multipart(form).send().await;
        match rs {
            Ok(res) => match res.json::<serde_json::Value>().await {
                Ok(v) => {
                    let result = v.get("result").and_then(Value::as_i64).unwrap_or(-100);
                    if result == 0 {
                        let name = v.get("name").and_then(Value::as_str).unwrap_or_default();
                        Ok(name.to_string())
                    } else {
                        Err(MyErr::Msg(format!("文件上传失败：错误码：{}！", result)))
                    }
                }
                Err(e) => {
                    log::error!("读取URL数据失败！错误：{:?} url:{} ", e, url);
                    Err(MyErr::Msg("网络异常！".to_string()))
                }
            },
            Err(e) => {
                log::error!("连接url地址失败！错误：{:?} url:{}", e, url);
                Err(MyErr::Msg("网络异常！".to_string()))
            }
        }
    }

    /// 上传司机图片
    #[allow(dead_code)]
    async fn upload_driver_img(&self, driver_img: &str) -> Result<String, MyErr> {
        //路径和 cargo.toml 在一个目录下面
        //let file = File::open("/home/laobu/Pictures/xwl.jpg").await?;
        let file = File::open(driver_img).await?;

        let file_body = file_to_body(file);

        let bio = reqwest::multipart::Part::stream(file_body)
            .file_name("driver.jpg")
            .mime_str("image/jpeg")
            .map_err(|e| MyErr::Msg(format!("读取上传文件失败：错误：{:?}！", e)))?;

        let form = reqwest::multipart::Form::new().part("upload", bio);

        let jsession = self.get_jsession().await?;

        let url = format!("{}{}?jsession={}", self.domain, UPLOAD_DRIVER_IMG_URL, jsession);

        let client = reqwest::Client::new();
        let rs = client.post(&url).multipart(form).send().await;
        match rs {
            Ok(res) => match res.json::<serde_json::Value>().await {
                Ok(v) => {
                    let result = v.get("result").and_then(Value::as_i64).unwrap_or(-100);
                    if result == 0 {
                        let name = v.get("name").and_then(Value::as_str).unwrap_or_default();
                        Ok(name.to_string())
                    } else {
                        Err(MyErr::Msg(format!("文件上传失败：错误码：{}！", result)))
                    }
                }
                Err(e) => {
                    log::error!("读取URL数据失败！错误：{:?} url:{} ", e, url);
                    Err(MyErr::Msg("网络异常！".to_string()))
                }
            },
            Err(e) => {
                log::error!("连接url地址失败！错误：{:?} url:{}", e, url);
                Err(MyErr::Msg("网络异常！".to_string()))
            }
        }
    }

    /// 上传司机信息
    #[allow(dead_code)]
    pub async fn import_driver_info(&self, name: &str, idcard: &str, phone: &str, driver_img: &str, company: &str) -> Result<i64, MyErr> {
        // 第一步，先上传司机相片
        let img_path = if driver_img.starts_with("http") {
            self.upload_driver_img_by_url(driver_img).await?
        } else {
            self.upload_driver_img(driver_img).await?
        };
        log::info!("\n\n\n 上传司机相片成功，地址：{}", img_path);

        let birth = util_string::get_birthday_by_idcard(idcard)?;
        let sex = util_string::get_sex_by_idcard(idcard)?;

        // 第二步，上传司机资料
        let jsession = self.get_jsession().await?;

        let form_data = json!({
            "jsession" : jsession,
            "jobNum":idcard,
            "name":name,
            "contact": phone,
            "cardNumber": idcard,
            "sex" : sex,  // 性别 1男 2女
            "licenseNum": idcard,
            "licenseType": "A1",
            "birth": birth,
            "rushDate": "2020-08-10",
            "startTime":"2020-08-10",
            "validity": "2030-08-10",
            "reminderDays": 0,
            "companyName": company,
            "facePhotoUrl": img_path,
        });

        let url = format!("{}{}", self.domain, EDIT_DRIVER_URL);
        let rs = post_from_data_to_url(&url, &form_data).await;
        match rs {
            Ok(res) => {
                let result = res.get("result").and_then(Value::as_i64).unwrap_or(-100);
                if result == 0 {
                    Ok(1)
                } else {
                    let message = res.get("message").and_then(Value::as_str).unwrap_or_default();
                    Err(MyErr::Msg(format!("文件上传失败：错误码：{}，描述：{}", result, message)))
                }
            }
            Err(e) => Err(MyErr::Msg(format!("连接url地址失败！错误：{:?} url:{} ", e, url))),
        }
    }

    /// 获取用户车辆信(获取设备信息)
    #[allow(dead_code)]
    pub async fn get_dev_info(&self) -> Result<(Vec<CompanyBean>, Vec<CarBean>), MyErr> {
        #[derive(Deserialize, Debug)]
        pub struct RsBean {
            pub result: i32,
            pub vehicles: Option<Vec<CarBean>>,
            pub companys: Option<Vec<CompanyBean>>,
        }

        let jsession = self.get_jsession().await?;

        let mut form_data = HashMap::new();
        form_data.insert("jsession", jsession);

        let url = format!("{}{}", self.domain, CAR_SEARCH_URL);
        let rs = post_from_data_to_url(&url, &form_data).await;
        match rs {
            Ok(res) => {
                if let Ok(rs_bean) = serde_json::from_value::<RsBean>(res) {
                    if rs_bean.result == 0 {
                        if let Some(vs) = rs_bean.vehicles {
                            if let Some(cs) = rs_bean.companys {
                                Ok((cs, vs))
                            } else {
                                Err(MyErr::Msg(format!("获取车辆信息失败！缺少公司信息！ url:{} ", url)))
                            }
                        } else {
                            Err(MyErr::Msg(format!("获取车辆信息失败！缺少车辆信息！ url:{} ", url)))
                        }
                    } else {
                        Err(MyErr::Msg(format!("获取车辆信息失败！错误码：{} url:{} ", rs_bean.result, url)))
                    }
                } else {
                    Err(MyErr::Msg(format!("获取车辆信息失败！解析数据错误! url:{} ", url)))
                }
            }
            Err(e) => Err(MyErr::Msg(format!("连接url地址失败！错误：{:?} url:{} ", e, url))),
        }
    }

    ///  获取报警附件信息
    /// dev_id : 设备号
    /// begintime：开始时间，开始时间不得大于结束时间。格式：yyyy-mm-dd
    /// alarm_type：报警类型
    /// guid：报警唯一编号
    #[allow(dead_code)]
    pub async fn get_alarm_attr(
        &self,
        dev_id: &str,
        begintime: &str,
        alarm_type: i32,
        guid: &str,
    ) -> Result<(AlarmAttrInfo, Option<Vec<AlarmAttrFile>>, Option<Vec<AlarmAttrFile>>), MyErr> {
        let jsession = self.get_jsession().await?;

        let mut form_data = HashMap::new();
        form_data.insert("jsession", jsession);
        form_data.insert("devIdno", dev_id.to_string());
        form_data.insert("begintime", begintime.to_string());
        form_data.insert("alarmType", alarm_type.to_string());
        form_data.insert("guid", guid.to_string());

        let url = format!("{}{}", self.domain, ALARM_EVIDENCE_URL);
        let rs = post_from_data_to_url(&url, &form_data).await;
        match rs {
            Ok(res) => {
                #[derive(Deserialize, Debug)]
                pub struct RsBean {
                    pub result: i32,
                    pub infos: AlarmAttrInfo,
                    pub images: Option<Vec<AlarmAttrFile>>,
                    pub vedios: Option<Vec<AlarmAttrFile>>,
                }

                if let Ok(rs) = serde_json::from_value::<RsBean>(res) {
                    if rs.result == 0 {
                        Ok((rs.infos, rs.images, rs.vedios))
                    } else {
                        Err(MyErr::Msg(format!("获取报警附件信息失败！错误码：{} url:{} ", rs.result, url)))
                    }
                } else {
                    Err(MyErr::Msg(format!("获取报警附件信息失败，无法解析服务器返回结果！ url:{} ", url)))
                }
            }
            Err(e) => Err(MyErr::Msg(format!("连接url地址失败！错误：{:?} url:{} ", e, url))),
        }
    }

    /// 获取车辆行驶里程,只能查询隔天里程
    /// vehiIdno：车牌号 ， 可以是多个，以','分割。中文需要做url编码
    /// begintime：开始时间，开始时间不得大于结束时间。格式：yyyy-mm-dd
    /// endtime：结束时间。格式：yyyy-mm-dd
    #[allow(dead_code)]
    pub async fn get_run_mileage(&self, vehi_idno: &str, begintime: &str, endtime: &str) -> Result<Vec<RunMileageBean>, MyErr> {
        let mut data: Vec<RunMileageBean> = Vec::new();

        let jsession = self.get_jsession().await?;

        let mut form_data = HashMap::new();
        form_data.insert("jsession", jsession);
        form_data.insert("vehiIdno", vehi_idno.to_string());
        form_data.insert("begintime", begintime.to_string());
        form_data.insert("endtime", endtime.to_string());

        let url = format!("{}{}", self.domain, RUN_MILEAGE_URL);
        let rs = post_from_data_to_url(&url, &form_data).await;
        match rs {
            Ok(res) => {
                let result = res.get("result").and_then(Value::as_i64).unwrap_or(-1);
                log::info!("\n\n\n\n\n res: {:?} \n\n\n\n\n", res);
                if result == 0 {
                    if let Some(infos) = res.get("infos").and_then(Value::as_array) {
                        for info in infos {
                            let veh_idno = info.get("vehIdno").and_then(Value::as_str).unwrap_or_default();
                            let vehi_id = info.get("vehiId").and_then(Value::as_i64).unwrap_or_default();
                            let mile = info.get("mile").and_then(Value::as_f64).unwrap_or_default();
                            let s_time_str = info.get("sTimeStr").and_then(Value::as_str).unwrap_or_default();
                            let e_time_str = info.get("eTimeStr").and_then(Value::as_str).unwrap_or_default();
                            let ttime = info.get("ttime").and_then(Value::as_f64).unwrap_or_default();
                            data.push(RunMileageBean {
                                veh_idno: veh_idno.to_string(),
                                mile: mile.round() as i64, // 四舍五入到最近的整数
                                vehi_id: vehi_id,
                                s_time_str: s_time_str.to_string(),
                                e_time_str: e_time_str.to_string(),
                                ttime: ttime.round() as i64,
                            });
                        }
                    }

                    Ok(data)
                } else {
                    Err(MyErr::Msg(format!("获取车辆行驶里程失败！错误码：{} url:{} ", result, url)))
                }
            }
            Err(e) => Err(MyErr::Msg(format!("连接url地址失败！错误：{:?} url:{} ", e, url))),
        }
    }

    /// 获取授权方令牌（authorization_access_token 即公众号的 access_token）
    async fn get_jsession(&self) -> Result<String, MyErr> {
        let ttx_jession_key = format!("{}_TTX_JID", self.account);
        if let Some(s) = my_cache::get(&ttx_jession_key) {
            // 如果在缓存中已经存在 jsession 则直接返回
            log::info!("缓存中已经存在 jsession ，直接获取并返回: {}", s);
            Ok(s)
        } else {
            // 如果缓存中没有，则登录接口获取

            let mut form_data = HashMap::new();
            form_data.insert("account", self.account.clone());
            form_data.insert("password", self.password.clone());

            let url = format!("{}{}", self.domain, LOGIN_URL);
            let rs = post_from_data_to_url(&url, &form_data).await;
            match rs {
                Ok(res) => {
                    let result = res.get("result").and_then(Value::as_i64).unwrap_or(-1);
                    if result == 0 {
                        let jsession = res.get("jsession").and_then(Value::as_str).unwrap_or_default();
                        let expires_in = 30 * 60; // 单位（秒），30分钟
                        my_cache::set(&ttx_jession_key, jsession.to_string(), expires_in);

                        Ok(jsession.to_string())
                    } else {
                        Err(MyErr::Msg(format!("登录失败！错误码：{} url:{} ", result, url)))
                    }
                }
                Err(e) => Err(MyErr::Msg(format!("连接url地址失败！错误：{:?} url:{} ", e, url))),
            }
        }
    }
}

/// 使用 Content-Type ：application/x-www-form-urlencoded 方式 post 数据
async fn post_from_data_to_url<T: Serialize + std::fmt::Debug + ?Sized>(url: &str, form_data: &T) -> Result<Value, MyErr> {
    log::info!("\n\n\n url:{} data:{:?} \n\n\n\n", url, form_data);

    let client = reqwest::Client::new();
    let rs = client.post(url).form(form_data).send().await;
    match rs {
        Ok(res) => match res.json::<serde_json::Value>().await {
            Ok(v) => {
                log::debug!("数据读取成功！：{:?} url:{} post_form_data:{:?}", v, url, form_data);
                Ok(v)
            }
            Err(e) => {
                log::error!("读取URL数据失败！错误：{:?} url:{} post_form_data:{:?}", e, url, form_data);
                Err(MyErr::Msg("网络异常！".to_string()))
            }
        },
        Err(e) => {
            log::error!("连接url地址失败！错误：{:?} url:{} post_form_data:{:?}", e, url, form_data);
            Err(MyErr::Msg("网络异常！".to_string()))
        }
    }
}

fn file_to_body(file: File) -> Body {
    let stream = FramedRead::new(file, BytesCodec::new());
    let body = Body::wrap_stream(stream);
    body
}

fn bytes_to_body(file: Bytes) -> Body {
    let body = Body::from(file);
    body
}
